package com.imu.purse.task;

import com.alibaba.fastjson.JSON;
import com.imu.constant.Constant;
import com.imu.purse.bean.CoreMoneyAddr;
import com.imu.purse.modules.bitcoin.YtfcoinApi;
import com.imu.purse.modules.bitcoin.form.YtfcoinForm;
import com.imu.purse.modules.trade.entity.ScheduleLogEntity;
import com.imu.purse.modules.trade.service.*;
import com.qiniu.util.StringUtils;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.LogFactory;
import com.xiaoleilu.hutool.util.StrUtil;
import org.ethereum.solidity.Abi;
import org.ethereum.util.ByteUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class YtfcoinRun {
    private static final Log log = LogFactory.get(YtfcoinRun.class);
    @Autowired
    private YtfcoinApi ytfcoinApi;

    @Autowired
    private ScheduleLogService scheduleLogService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Autowired
    private TradeInfoService tradeInfoService;

    @Autowired
    private TransDetailLogService transDetailLogService;

    @Autowired
    private TransLogService transLogService;

    @Autowired
    private FundService fundService;

    @Autowired
    private YtfTransInService ytfTransInService;


    private final String  tmpUserId = "userId";

    public void handleData(String coreStr){
        CoreMoneyAddr coreMoneyAddr = JSON.parseObject(coreStr,CoreMoneyAddr.class);
        String scheduleName = Constant.YTFCOIN_SCHEDULE_NAME + "-" + (coreMoneyAddr.getUrl()+":"+coreMoneyAddr.getPort()).hashCode();
        //从数据字典中获取已处理的高度
        Integer bHeight = 0;
        try{
            //从缓存中获取最后一次更新的高度
            String rHeight = redisTemplate.opsForValue().get(Constant.YTFCOIN_HEIGHT + "-" + (coreMoneyAddr.getUrl()+":"+coreMoneyAddr.getPort()).hashCode());
            if(!StringUtils.isNullOrEmpty(rHeight)){//缓存中有数据
                bHeight = Integer.valueOf(rHeight);
            }else{
                //缓存中没有数据从数据库获取
                bHeight = scheduleLogService.findMaxByNameAndType(scheduleName, Constant.ScheduleStatus.SUCCESS);
            }

        }catch(Exception e){
            log.error(e);
            //从数据库获取最后一次已处理的高度
            bHeight = scheduleLogService.findMaxByNameAndType(scheduleName, Constant.ScheduleStatus.SUCCESS);
        }
        bHeight = bHeight == null ? 0 : bHeight;

        int startHeight = bHeight;
        Timestamp startTime = new Timestamp(System.currentTimeMillis());
        //获取区块高度
        Integer bCount = ytfcoinApi.getblockcount(coreStr);
        if(bCount == null || bCount == 0){
            log.info("获取区块高度失败");
            return;
        }

        Map<String,Integer> resultMap = new HashMap<>(1);
        resultMap.put("status", 1);//默认成功
        while(bHeight <= bCount - 1){
            if(resultMap.get("status") == 0){
                log.info("钱包连接异常.......................");
                break;
            }
            //根据高度获取区块信息
            Map<String,Object> rMap  = ytfcoinApi.getBlock(bHeight,coreStr);
            bHeight++;
            if(CollectionUtils.isEmpty(rMap)){
                continue;
            }

            if(rMap.get("error") != null  && rMap.get("error").toString().equals(Constant.MONEY_PACKAGE_CONNECT_OTHER_ERROR)) {
                continue;
            }else if(rMap.get("error") != null  && Constant.MONEY_PACKAGE_CONNECT_ERROR.equals(rMap.get("error").toString())){//钱包连接异常
                resultMap.put("status", 0);
                break;
            }
            //获取当前块的时间
            String curTime = rMap.get("timestamp").toString();
            curTime = curTime.startsWith("0x") ? curTime.substring(2) : curTime;
            Long bTime = Long.valueOf(curTime,16);
            //获取交易信息
            Object obj = rMap.get("transactions");
            if(obj != null){
                List<Map<String,String>> tList = (List<Map<String,String>>)obj;
                handleData(tList,bTime,coreMoneyAddr);
            }
        }
        if(resultMap.get("status") == 0){
            //发送短信
            return;
        }
        Timestamp endTime = new Timestamp(System.currentTimeMillis());
        //将本次的高度保存到缓存中
        redisTemplate.opsForValue().set(Constant.YTFCOIN_HEIGHT + "-" + (coreMoneyAddr.getUrl() + ":" + coreMoneyAddr.getPort()).hashCode(),bCount+"");
        //保存数据
        saveScheduleData(scheduleName,resultMap,startHeight,bCount,startTime,endTime);
    }

    /**
     * 将交易ID推送到消息队列中
     * @param tList
     * @param blockTime
     * @param moneyAddr
     */
    private void handleData(List<Map<String,String>> tList ,Long blockTime,CoreMoneyAddr moneyAddr){
        if(CollectionUtils.isEmpty(tList)){
            return;
        }
        //数据可能来自以太坊或者代币，如果以太坊，to则直接对应我们数据库的地址
        //如果代币，则为IMU的合约地址   包括转入，转出，转入的时候，针对IMU时
        //input中为我们这边小钱包地址，针对以太坊的时候，to为我们这边小地址

        for(Map<String,String> data : tList){

            data.put("tradeTime",blockTime + "");
            //A --A1  以太坊  from :A  to: 合约地址 input：由A1+money加密   A转以太坊A1  归集
            //A1 --A  代币    FROM : A1,TO:A   A1转代币A1   归集
            //A-B1 Form : A TO:B1  //A1转B1
            //B-A1 From:B TO:A1   接受
            //转出时候，from为这边核心钱包大钱包的地址
            String from = data.get("from");
            if(StrUtil.isEmpty(from)){
                continue;
            }
            //获取用户ID
            String fObj = fundService.getCordeByAddress(from);
            String to = data.get("to");
            if(StrUtil.isEmpty(to)){
                continue;
            }
            //获取用户ID
            String toObj = fundService.getCordeByAddress(to);
            int dataType = 0;

            //log.error("from===========" + from + ",to===" + to);

            if(from.equalsIgnoreCase(Constant.YTF_FROM) && toObj != null){//A-->A1 转原生币
                //修改trans_log,trans_log_detail表信息地址
                //from : A to:A1 data没有数据
                data.put(tmpUserId,toObj.toString());
                dataType = 1;

            }else if(!from.equalsIgnoreCase(Constant.YTF_FROM) && fObj != null
                    && to.equalsIgnoreCase(Constant.IMU_CONTRACT_ADDRESS)){ //A1 -- > A 代币
                //修改trans_log_detail表信息转代币
                // data中为A的地址,from:A1,to:合约地址
                data.put(tmpUserId,fObj.toString()); //A1地址
                dataType = 2;
            }else if(from.equalsIgnoreCase(Constant.YTF_FROM) &&
                    to.equalsIgnoreCase(Constant.IMU_CONTRACT_ADDRESS)){//A-->B1 代币 向外转出
                //修改trade_info信息，并且修改fund表信息
                //from :A to:合约地址 data:B1地址
                dataType = 3;
            }else if(!from.equalsIgnoreCase(Constant.IMU_CONTRACT_ADDRESS) && fObj == null &&
                    toObj != null){//转入 B-->A1原生币
                //ytf_tran_in 插入
                //from :B to:A1,data没有数据
                data.put(tmpUserId,toObj.toString());
                data.put("payAddr",from);
                data.put("recAddr",to);
                dataType = 4;
            } else if(!from.equalsIgnoreCase(Constant.IMU_CONTRACT_ADDRESS) && fObj == null
                    && to.equalsIgnoreCase(Constant.IMU_CONTRACT_ADDRESS)){//B-->A1代币
                //from : B to:IMU和约地址,data:A1
                //修改trade_info信息，并且修改fund表信息
                data.put("recAddr",from);
                dataType = 5;
            } else if(from.equalsIgnoreCase(Constant.IMU_CONTRACT_ADDRESS) && toObj == null){//A（公司账号）转以太坊到B1
                continue;//暂时不考虑 IMU以太坊都是初始化的
            }else if(!from.equalsIgnoreCase(Constant.IMU_CONTRACT_ADDRESS) && fObj != null && toObj == null){//A1转以太坊B1
                continue; //目前用户账号不存在以太坊
            } else{
                continue;
            }
            if(!configData(data,dataType)){
                continue;
            }
            data.put("dataType",dataType + "");
            //修改用户金额
            updateFund(data);

        }
    }

    /**
     * 构建数据
     * @param dataMap
     * @param dataFlag   1:A-->A1 原生币 2: A1-->A 代生币 3:A-->B1 代币,4:B-->A1原生币 5:B-->A1代币
     * @return
     */
    private boolean configData(Map<String,String> dataMap,int dataFlag){
        //2: A1-->A 代生币  3:A-->B1 代币 5:B-->A1代币
        if(dataFlag == 5 || dataFlag == 2 || dataFlag == 3){
            //解析接受人钱包地址和金额
            List<?>  params = null;
            try{
                params = Abi.fromJson(Constant.IMU_ABI).findFunction(function -> "transfer".equals(function.name))
                        .decode(ByteUtil.hexStringToBytes(dataMap.get("input")));

            }catch (Exception e){
                log.error(e);
                return false;
            }
            if(CollectionUtils.isEmpty(params) || params.size() != 2){
                return false;
            }
            byte[] addressByte = (byte[])params.get(0);//获取地址
            String toAddress = ByteUtil.toHexString(addressByte);//解析地址
            //A1地址
            toAddress = toAddress.startsWith(Constant.YTF_PERFIX) ? toAddress : Constant.YTF_PERFIX + toAddress;
            String userIdObj = fundService.getCordeByAddress(toAddress);//该地址是否是imu国际钱包的
            if(StrUtil.isBlank(userIdObj) && (dataFlag == 2 || dataFlag == 5)){//转2: A1-->A 代生币  5:B-->A1代币
                return false;
            }
            BigInteger money = (BigInteger)params.get(1);//获取金额
            BigDecimal bMoney = new BigDecimal(money);
            bMoney = bMoney.divide(new BigDecimal(Constant.MONEY_TO_YTF)); //将金额转换为10进账
            bMoney.setScale(9,BigDecimal.ROUND_HALF_DOWN);//保留9位小数
            dataMap.put("money",bMoney.toString());
            if(dataFlag == 2 || dataFlag == 5){///转2: A1-->A 代生币  5:B-->A1代币
                dataMap.put(tmpUserId,userIdObj);
            }

            return true;
        }else{//4:B-->A1 原生币  1:A-->A1 原生币
            BigInteger money = new BigInteger(dataMap.get("value").substring(2),16);
            BigDecimal bMoney = new BigDecimal(money);
            bMoney = bMoney.divide(new BigDecimal(Constant.MONEY_TO_YTF));
            bMoney.setScale(9,BigDecimal.ROUND_HALF_DOWN);
            dataMap.put("money",bMoney.toString());
        }

        return true;
    }
    /**
     * 保存定时任务记录
     * @param scheduleName
     * @param resultMap
     * @param startHeight
     * @param endHeight
     * @param startTime
     * @param endTime
     */
    private void saveScheduleData(String scheduleName,Map<String,Integer> resultMap,
                                  Integer startHeight,Integer endHeight,Timestamp startTime,
                                  Timestamp endTime){
        ScheduleLogEntity entity = new ScheduleLogEntity();
        entity.setScheduleName(scheduleName);
        entity.setStatus(resultMap.get("status"));
        entity.setStartHeight(startHeight);
        entity.setEndHeight(endHeight);
        entity.setStartTime(startTime);
        entity.setEndTime(endTime);
        entity.setTimeCreate(new Date());
        scheduleLogService.save(entity);
    }

    /**
     * 修改用户金额
     * @param dataMap
     */
    private void updateFund(Map<String,String> dataMap){

        YtfcoinForm form = new YtfcoinForm();
        form.setTransTime(Long.valueOf(dataMap.get("tradeTime")));
        form.setTransId(dataMap.get("hash"));
        form.setMoney(new BigDecimal(dataMap.get("money")));
        form.setPayAddr(dataMap.get("payAddr"));
        form.setRecAddr(dataMap.get("recAddr"));
        //form.setAddress(dataMap.get("address"));
        int dataType = Integer.valueOf(dataMap.get("dataType"));
        String userId = dataMap.get(tmpUserId);
        if(dataType == 1){//A --A1 原生币  修改表trans_log trans_log_detail
            form.setUserId(Long.valueOf(userId));
            //根据userId,transId,时间修改trans_log
            //根据trans_id和时间修改trade_log_detail数据
            transLogService.moYtfLog(form);
        }else if(dataType == 2){//A1-->A 代币 修改表trans_log_detail,可能需要修改trans_log
            //根据trans_id和时间修改trade_log_detail数据 并获取log_id
            //根据log_id和时间修改trans_log
            transDetailLogService.moYtfLog(form);
        }else if(dataType == 3){//A-->B1  修改表trade_info,fund表
            //根据transId和时间修改trade_info,获取userId,money,fee
            //根据userId,money,fee修改imu_fund表
            //form.setUserId(Long.valueOf(userId));
            tradeInfoService.modYtfOutTransInfo(form);
        }else if(dataType == 4){//B-->A1 原生币 修改表fund ,增加trade_info
            //ytf_trans_in插入数据 存在则不插入
            form.setUserId(Long.valueOf(userId));
            ytfTransInService.ytfTransInData(form);
        }else if(dataType == 5){//B-->A1
            //根据用户ID修改imu_fund,并增加trade_info
            form.setUserId(Long.valueOf(userId));
            tradeInfoService.modYtfInTranInfo(form);
        }



    }

    public static void main(String[] args){

        BigInteger money = new BigInteger("0x62cf5246d0c8000".substring(2),16);
        BigDecimal bMoney = new BigDecimal(money);
        bMoney = bMoney.divide(new BigDecimal(Constant.MONEY_TO_YTF));
        bMoney.setScale(9,BigDecimal.ROUND_HALF_DOWN);
        System.out.println(bMoney);
    }
}
